﻿using ProgramCat.FiddlerExtension.Core.Utils;
using ScintillaNET;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ProgramCat.FiddlerExtension.Core.JsEditor
{
    /// <summary>
    /// JS编辑器 基于Notepad++
    /// </summary>
    public class JsEditor
    {

        public ScintillaNET.Scintilla scintilla;

        //----------TextCodeJs------------
        /// <summary>
        /// 将此值更改为要显示行号的任何边距
        /// </summary>
        public const int NUMBER_MARGIN = 2;
        /// <summary>
        /// 将其更改为希望书签/断点显示在其中的任何边距
        /// </summary>
        public const int BOOKMARK_MARGIN = 0;
        public const int BOOKMARK_MARKER = 0;
        //背景颜色
        public int BACK_COLOR = 0x313335;// SystemColors.Window.ToArgb();
        //文字颜色
        public int FORE_COLOR = 0x7A7C7F;//Color.Black.ToArgb();
        /// <summary>
        /// 将其更改为希望代码折叠树（+/-）显示的任何边距
        /// </summary>
        public const int FOLDING_MARGIN = 3;
        /// <summary>
        /// 如果设置为true，则显示用于代码折叠的圆形按钮（边距上的[+]和[-]按钮）
        /// </summary>
        public const bool CODEFOLDING_CIRCULAR = false;

        public JsEditor(Panel TextPanel_JsCode)
        {
            //创建控件
            scintilla = new ScintillaNET.Scintilla();
            //监听事件
            scintilla.CharAdded += scintilla_CharAdded;
            scintilla.UpdateUI += scintilla_UpdateUI;
            //scintilla.TextChanged += scintilla_TextChanged;

            TextPanel_JsCode.Controls.Add(scintilla);
            //基本配置
            scintilla.Dock = System.Windows.Forms.DockStyle.Fill;
            //初始视图配置
            scintilla.WrapMode = WrapMode.Word;
            scintilla.IndentationGuides = IndentView.LookBoth;
            //样式
            InitTextCodeJsColors();
            //语法着色器
            InitTextCodeJsSyntaxColoring();
            //行号边距
            InitTextCodeJsSyntaxNumberMargin();
            //书签边距
            InitTextCodeJsBookmarkMargin();
            //代码折叠边距
            InitTextCodeJsCodeFolding();
            //初始化热键
            InitTextCodeJsHotkeys();
        }


        /// <summary>
        /// 初始化文本颜色
        /// </summary>
        private void InitTextCodeJsColors()
        {
            scintilla.SetSelectionBackColor(true, ColorHelper.IntToColor(0x114D9C));
            scintilla.CaretForeColor = Color.White;
        }
        /// <summary>
        /// 初始化js语法着色器
        /// </summary>
        private void InitTextCodeJsSyntaxColoring()
        {

            // Configure the default style
            scintilla.StyleResetDefault();
            scintilla.Styles[Style.Default].Font = "Consolas";
            scintilla.Styles[Style.Default].Size = 10;
            scintilla.Styles[Style.Default].BackColor = ColorHelper.IntToColor(0x212121);
            scintilla.Styles[Style.Default].ForeColor = ColorHelper.IntToColor(0xFFFFFF);
            scintilla.StyleClearAll();

            // Configure the CPP (C#) lexer styles
            scintilla.Styles[Style.Cpp.Identifier].ForeColor = ColorHelper.IntToColor(0xD0DAE2);
            scintilla.Styles[Style.Cpp.Comment].ForeColor = ColorHelper.IntToColor(0xBD758B);
            scintilla.Styles[Style.Cpp.CommentLine].ForeColor = ColorHelper.IntToColor(0x40BF57);
            scintilla.Styles[Style.Cpp.CommentDoc].ForeColor = ColorHelper.IntToColor(0x2FAE35);
            scintilla.Styles[Style.Cpp.Number].ForeColor = ColorHelper.IntToColor(0xFFFF00);
            scintilla.Styles[Style.Cpp.String].ForeColor = ColorHelper.IntToColor(0xFFFF00);
            scintilla.Styles[Style.Cpp.Character].ForeColor = ColorHelper.IntToColor(0xE95454);
            scintilla.Styles[Style.Cpp.Preprocessor].ForeColor = ColorHelper.IntToColor(0x8AAFEE);
            scintilla.Styles[Style.Cpp.Operator].ForeColor = ColorHelper.IntToColor(0xE0E0E0);
            scintilla.Styles[Style.Cpp.Regex].ForeColor = ColorHelper.IntToColor(0xff00ff);
            scintilla.Styles[Style.Cpp.CommentLineDoc].ForeColor = ColorHelper.IntToColor(0x77A7DB);
            scintilla.Styles[Style.Cpp.Word].ForeColor = ColorHelper.IntToColor(0x48A8EE);
            scintilla.Styles[Style.Cpp.Word2].ForeColor = ColorHelper.IntToColor(0xF98906);
            scintilla.Styles[Style.Cpp.CommentDocKeyword].ForeColor = ColorHelper.IntToColor(0xB3D991);
            scintilla.Styles[Style.Cpp.CommentDocKeywordError].ForeColor = ColorHelper.IntToColor(0xFF0000);
            scintilla.Styles[Style.Cpp.GlobalClass].ForeColor = ColorHelper.IntToColor(0x48A8EE);


            scintilla.Styles[Style.BraceLight].BackColor = Color.LightGray;
            scintilla.Styles[Style.BraceLight].ForeColor = Color.BlueViolet;
            scintilla.Styles[Style.BraceBad].ForeColor = Color.Red;

            scintilla.Lexer = Lexer.Cpp;
            //这里应该使用资源文本
            scintilla.SetKeywords(0, "class extends implements import interface new case do while else if for in switch throw get set function var try catch finally while with default break continue delete return each const namespace package include use is as instanceof typeof author copy default deprecated eventType example exampleText exception haxe inheritDoc internal link mtasc mxmlc param private return see serial serialData serialField since throws usage version langversion playerversion productversion dynamic private public partial static intrinsic internal native override protected AS3 final super this arguments null Infinity NaN undefined true false abstract as base bool break by byte case catch char checked class const continue decimal default delegate do double descending explicit event extern else enum false finally fixed float for foreach from goto group if implicit in int interface internal into is lock long new null namespace object operator out override orderby params private protected public readonly ref return switch struct sbyte sealed short sizeof stackalloc static string select this throw true try typeof uint ulong unchecked unsafe ushort using var virtual volatile void while where yield string int");
            scintilla.SetKeywords(1, "Array Date eval hasOwnProperty Infinity isFinite isNaN isPrototypeOf Math NaN Number Object prototype String toString undefined valueOf");


        }
        private void InitTextCodeJsSyntaxNumberMargin()
        {

            scintilla.Styles[Style.LineNumber].BackColor = ColorHelper.IntToColor(BACK_COLOR);
            scintilla.Styles[Style.LineNumber].ForeColor = ColorHelper.IntToColor(FORE_COLOR);
            scintilla.Styles[Style.IndentGuide].ForeColor = ColorHelper.IntToColor(FORE_COLOR);
            scintilla.Styles[Style.IndentGuide].BackColor = ColorHelper.IntToColor(BACK_COLOR);

            var nums = scintilla.Margins[NUMBER_MARGIN];
            nums.Width = 30;
            nums.Type = MarginType.Number;
            nums.Sensitive = true;
            nums.Mask = 0;

            scintilla.MarginClick += TextCodeJs_MarginClick;
        }
        private void InitTextCodeJsBookmarkMargin()
        {

            //TextArea.SetFoldMarginColor(true, IntToColor(BACK_COLOR));
            var margin = scintilla.Margins[BOOKMARK_MARGIN];
            margin.Width = 0;
            margin.Sensitive = true;
            margin.Type = MarginType.Symbol;
            margin.Mask = (1 << BOOKMARK_MARKER);
            //margin.Cursor = MarginCursor.Arrow;
            var marker = scintilla.Markers[BOOKMARK_MARKER];
            marker.Symbol = MarkerSymbol.Circle;
            marker.SetBackColor(ColorHelper.IntToColor(0xFF003B));
            marker.SetForeColor(ColorHelper.IntToColor(0x000000));
            marker.SetAlpha(100);

        }
        private void InitTextCodeJsCodeFolding()
        {

            scintilla.SetFoldMarginColor(true, ColorHelper.IntToColor(BACK_COLOR));
            scintilla.SetFoldMarginHighlightColor(true, ColorHelper.IntToColor(BACK_COLOR));

            // Enable code folding
            scintilla.SetProperty("fold", "1");
            scintilla.SetProperty("fold.compact", "1");

            // Configure a margin to display folding symbols
            scintilla.Margins[FOLDING_MARGIN].Type = MarginType.Symbol;
            scintilla.Margins[FOLDING_MARGIN].Mask = Marker.MaskFolders;
            scintilla.Margins[FOLDING_MARGIN].Sensitive = true;
            scintilla.Margins[FOLDING_MARGIN].Width = 20;

            // Set colors for all folding markers
            for (int i = 25; i <= 31; i++)
            {
                scintilla.Markers[i].SetForeColor(ColorHelper.IntToColor(BACK_COLOR)); // styles for [+] and [-]
                scintilla.Markers[i].SetBackColor(ColorHelper.IntToColor(FORE_COLOR)); // styles for [+] and [-]
            }

            // Configure folding markers with respective symbols
            scintilla.Markers[Marker.Folder].Symbol = CODEFOLDING_CIRCULAR ? MarkerSymbol.CirclePlus : MarkerSymbol.BoxPlus;
            scintilla.Markers[Marker.FolderOpen].Symbol = CODEFOLDING_CIRCULAR ? MarkerSymbol.CircleMinus : MarkerSymbol.BoxMinus;
            scintilla.Markers[Marker.FolderEnd].Symbol = CODEFOLDING_CIRCULAR ? MarkerSymbol.CirclePlusConnected : MarkerSymbol.BoxPlusConnected;
            scintilla.Markers[Marker.FolderMidTail].Symbol = MarkerSymbol.TCorner;
            scintilla.Markers[Marker.FolderOpenMid].Symbol = CODEFOLDING_CIRCULAR ? MarkerSymbol.CircleMinusConnected : MarkerSymbol.BoxMinusConnected;
            scintilla.Markers[Marker.FolderSub].Symbol = MarkerSymbol.VLine;
            scintilla.Markers[Marker.FolderTail].Symbol = MarkerSymbol.LCorner;

            // Enable automatic folding
            scintilla.AutomaticFold = (AutomaticFold.Show | AutomaticFold.Click | AutomaticFold.Change);

        }
        /// <summary>
        /// 快捷键设置
        /// </summary>
        private void InitTextCodeJsHotkeys()
        {

          
            scintilla.ClearCmdKey(Keys.Control | Keys.F);
            scintilla.ClearCmdKey(Keys.Control | Keys.R);
            scintilla.ClearCmdKey(Keys.Control | Keys.H);
            scintilla.ClearCmdKey(Keys.Control | Keys.L);
            scintilla.ClearCmdKey(Keys.Control | Keys.U);

        }
        private void TextCodeJs_ZoomOut()
        {
            scintilla.ZoomOut();
        }
        private void TextCodeJs_ZoomIn()
        {
            scintilla.ZoomIn();
        }


        private void TextCodeJs_MarginClick(object sender, MarginClickEventArgs e)
        {
            if (e.Margin == 2)
            {
                // Do we have a marker for this line?
                const uint mask = (1 << 2);
                var line = scintilla.Lines[scintilla.LineFromPosition(e.Position)];
                if ((line.MarkerGet() & mask) > 0)
                {
                    // Remove existing bookmark
                    line.MarkerDelete(2);
                }
                else
                {
                    // Add bookmark
                    line.MarkerAdd(2);
                }
            }
        }
        private static bool IsBrace(int c)
        {
            switch (c)
            {
                case '(':
                case ')':
                case '[':
                case ']':
                case '{':
                case '}':
                case '<':
                case '>':
                    return true;
            }

            return false;
        }
        //输入事件
        private void scintilla_CharAdded(object sender, CharAddedEventArgs e)
        {
            var currentPos = scintilla.CurrentPosition;
            var wordStartPos = scintilla.WordStartPosition(currentPos, true);
            var lenEntered = currentPos - wordStartPos;
            if (lenEntered > 0)
            {
                if (!scintilla.AutoCActive)
                    scintilla.AutoCShow(lenEntered, Config.Instance.GetJsAutoCompletion());
            }
        }
        //UI刷新事件
        int lastCaretPos = 0;
        private void scintilla_UpdateUI(object sender, UpdateUIEventArgs e)
        {
            // Has the caret changed position?
            var caretPos = scintilla.CurrentPosition;
            if (lastCaretPos != caretPos)
            {
                lastCaretPos = caretPos;
                var bracePos1 = -1;
                var bracePos2 = -1;

                // Is there a brace to the left or right?
                if (caretPos > 0 && IsBrace(scintilla.GetCharAt(caretPos - 1)))
                    bracePos1 = (caretPos - 1);
                else if (IsBrace(scintilla.GetCharAt(caretPos)))
                    bracePos1 = caretPos;

                if (bracePos1 >= 0)
                {
                    // Find the matching brace
                    bracePos2 = scintilla.BraceMatch(bracePos1);
                    if (bracePos2 == Scintilla.InvalidPosition)
                        scintilla.BraceBadLight(bracePos1);
                    else
                        scintilla.BraceHighlight(bracePos1, bracePos2);
                }
                else
                {
                    scintilla.BraceHighlight(Scintilla.InvalidPosition, Scintilla.InvalidPosition);
                }
            }
        }
        public string LastSearch = "";
        public int LastSearchIndex;
        /// <summary>
        /// 搜索
        /// </summary>
        /// <param name="TextBox_Find"></param>
        /// <param name="next"></param>
        /// <param name="incremental"></param>
        public void SearchFind(TextBox TextBox_Find, bool next, bool incremental)
        {
            bool first = LastSearch != TextBox_Find.Text;
            incremental = first;
            LastSearch = TextBox_Find.Text;
            if (LastSearch.Length > 0)
            {
                if (next)
                {
                    scintilla.TargetStart = LastSearchIndex - 1;
                    scintilla.TargetEnd = LastSearchIndex + (LastSearch.Length + 1);
                    scintilla.SearchFlags = SearchFlags.None;
                    if (!incremental || scintilla.SearchInTarget(LastSearch) == -1)
                    {
                        scintilla.TargetStart = scintilla.CurrentPosition;
                        scintilla.TargetEnd = scintilla.TextLength;
                        scintilla.SearchFlags = SearchFlags.None;
                        if (scintilla.SearchInTarget(LastSearch) == -1)
                        {
                            scintilla.TargetStart = 0;
                            scintilla.TargetEnd = scintilla.TextLength;
                            if (scintilla.SearchInTarget(LastSearch) == -1)
                            {
                                scintilla.ClearSelections();
                                return;
                            }
                        }
                    }
                }
                else
                {
                    scintilla.TargetStart = 0;
                    scintilla.TargetEnd = scintilla.CurrentPosition;
                    scintilla.SearchFlags = SearchFlags.None;
                    if (scintilla.SearchInTarget(LastSearch) == -1)
                    {
                        scintilla.TargetStart = scintilla.CurrentPosition;
                        scintilla.TargetEnd = scintilla.TextLength;
                        if (scintilla.SearchInTarget(LastSearch) == -1)
                        {
                            scintilla.ClearSelections();
                            return;
                        }
                    }
                }
                LastSearchIndex = scintilla.TargetStart;
                scintilla.SetSelection(scintilla.TargetEnd, scintilla.TargetStart);
                scintilla.ScrollCaret();

            }

            TextBox_Find.Focus();
        }

    }
}
